//
//  HistogramView.m
//  Task Views
//
//  Created by Chris Karr on 3/18/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "HistogramView.h"
#import "HistogramBlockView.h"

@implementation HistogramView

- (id)initWithFrame:(NSRect) frame 
{
    if (self = [super initWithFrame:frame]) 
	{
		attributeLabel = nil;
		countLabel = nil;
		
		bins = [[NSMutableDictionary alloc] init];
		blocks = [[NSMutableArray alloc] init];
    }
	
    return self;
}

- (void) drawBackground:(NSRect) rect
{
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	NSData * color = [defaults valueForKey:@"hist_background"];
	
	if (color == nil)
		[[NSColor whiteColor] setFill];
	else
		[[NSKeyedUnarchiver unarchiveObjectWithData:color] setFill];
	
	NSBezierPath * path = [NSBezierPath bezierPathWithRect:rect];
	
	[path fill];
}

- (void) drawAxes:(NSRect) rect
{
	NSBezierPath * path = [NSBezierPath bezierPath];
	
	[path setLineWidth:1.0];
	[path setLineCapStyle:NSSquareLineCapStyle];
	[path setLineJoinStyle:NSMiterLineJoinStyle];
	[[NSColor blackColor] setStroke];
	
	[path moveToPoint:NSMakePoint(rect.origin.x, rect.size.height)];
	[path lineToPoint:NSMakePoint(rect.origin.x, rect.origin.y)];
	[path lineToPoint:NSMakePoint(rect.size.width, rect.origin.y)];
	
	[path stroke];
}

- (void) drawRect:(NSRect) rect 
{
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];

	[self drawBackground:rect];
	
	if (attributeLabel == nil)
	{
		attributeLabel = [[NSTextField alloc] init];
			
		[attributeLabel setBezeled:NO];
		[attributeLabel setBordered:NO];
		[attributeLabel setEditable:NO];
		[attributeLabel setDrawsBackground:NO];
	
		[self addSubview:attributeLabel];
		
		[self setNeedsDisplay:YES];
	}

	if ([defaults stringForKey:@"hist_attribute"] != nil)
		[attributeLabel setStringValue:[NSString stringWithFormat:@"%@ (%d values)", [defaults stringForKey:@"hist_attribute"],
										categoryCount, nil]];
	else
		[attributeLabel setStringValue:@"No Property Selected"];
	
	[attributeLabel sizeToFit];

	if (countLabel == nil)
	{
		countLabel = [[NSTextField alloc] init];
		[countLabel setBezeled:NO];
		[countLabel setBordered:NO];
		[countLabel setEditable:NO];
		[countLabel setDrawsBackground:NO];
		
		[self addSubview:countLabel];
		[countLabel rotateByAngle:-90.0];
		[countLabel sizeToFit];
		
		[self setNeedsDisplay:YES];
	}

	[countLabel setStringValue:[NSString stringWithFormat:@"Frequency (max = %d)", maxCategoryCount, nil]];
	[countLabel sizeToFit];

	NSRect axes = NSMakeRect(0, 0, [self frame].size.width, [self frame].size.height);

	if (NSEqualRects(rect, [self bounds]) || NSContainsRect(rect, [self bounds]))
	{
		NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
		
		NSString * attribute = @"   Unknown Property";
		
		if ([defaults stringForKey:@"hist_attribute"] != nil)
			attribute = [defaults stringForKey:@"hist_attribute"];

		CGFloat xOffset = ([self bounds].size.width - [attributeLabel bounds].size.width) / 2;
		[attributeLabel setFrame:NSMakeRect(xOffset, 10, [attributeLabel bounds].size.width, [attributeLabel bounds].size.height)];		

		CGFloat yOffset = ([self bounds].size.height - [countLabel bounds].size.height) / 2;
		[countLabel setFrame:NSMakeRect(10, yOffset, [countLabel bounds].size.width, [countLabel bounds].size.height)];		

		axes.size.width = axes.size.width - ([countLabel bounds].size.height + 20);
		axes.origin.x = floor(axes.origin.x + [attributeLabel bounds].size.height + 20) - 0.5;

		axes.size.height = axes.size.height - ([attributeLabel bounds].size.height + 20);
		axes.origin.y = floor(axes.origin.y + [attributeLabel bounds].size.height + 20) - 0.5;

		CGFloat blockWidth = floor((axes.size.width - ([countLabel bounds].size.height + 20)) / categoryCount);
		CGFloat blockHeight = floor((axes.size.height - ([attributeLabel bounds].size.height + 20)) / maxCategoryCount);
		
		if (blockHeight < 1)
			blockHeight = 1;

		if (blockWidth < 1)
			blockWidth = 1;

		CGFloat x = ceil(axes.origin.x);
		NSUInteger blockIndex = 0;

		for (NSString * key in [[bins allKeys] sortedArrayUsingSelector:@selector(compare:)])
		{
			CGFloat y = ceil(axes.origin.y);

			NSArray * matches = [bins valueForKey:key];
		
			for (NSDictionary * task in matches)
			{
				HistogramBlockView * block = [blocks objectAtIndex:blockIndex];
				block.task = task;
				
				[block setToolTip:[NSString stringWithFormat:@"%@ (%@: %@)", [task valueForKey:@"name"], attribute, key, nil]];
				
				[block setFrame:NSMakeRect(x, y, blockWidth, blockHeight)];
			
				blockIndex = blockIndex + 1;
			
				y = y + blockHeight;
			}
		
			x = x + blockWidth;
		}
	}
	
	[super drawRect:rect];

	[self drawAxes:axes];
}

- (void) setSelectedItem:(NSDictionary *) task
{
	[tasks setSelectedObjects:[NSArray arrayWithObject:task]];
}

- (void) stuffBins
{
	[bins removeAllObjects];
	
	for (NSView * v in blocks)
		[v removeFromSuperview];
	
	[blocks removeAllObjects];
	
	NSDate * startDate = nil;
	NSDate * endDate = nil;
	
	NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
	[dateFormatter setDateFormat:@"yyyy-MM-dd"];
	
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	
	BOOL ignoreMissing = [defaults boolForKey:@"hist_ignore_missing"];
	
	NSString * key = @"   Unknown Property";
	
	if ([defaults stringForKey:@"hist_attribute"] != nil)
		key = [defaults stringForKey:@"hist_attribute"];
	
	for (NSDictionary * task in [tasks arrangedObjects])
	{
		id value = [task valueForKey:key];
		
		if (value == nil)
		{
			if (!ignoreMissing)
				value = @"   Unknown Property";
		}
		
		if ([value isKindOfClass:[NSDate class]])
		{
			if (startDate == nil)
				startDate = value;
			
			if (endDate == nil)
				endDate = value;
			
			if ([startDate compare:value] == NSOrderedDescending)
				startDate = value;
			
			if ([endDate compare:value] == NSOrderedAscending)
				endDate = value;
			
			value = [dateFormatter stringFromDate:value];
		}
		
		
		if (value != nil)
		{
			NSMutableArray * matches = [bins valueForKey:[value description]];
			
			if (matches == nil)
			{
				matches = [NSMutableArray array];
				[bins setValue:matches forKey:[value description]];
			}
			
			[matches addObject:task];
			
			NSView * v = [[HistogramBlockView alloc] init];
			[blocks addObject:v];
			[self addSubview:v];
			
			[self setNeedsDisplay:YES];
		}
	}
	
	if (startDate != nil && endDate != nil)
	{
		NSDate * day = [[NSDate alloc] initWithTimeInterval:(60 * 60 * 24) sinceDate:startDate];
		
		while ([endDate compare:day] == NSOrderedDescending)
		{
			NSString * dayString = [dateFormatter stringFromDate:day];
			
			if ([bins valueForKey:dayString] == nil)
				[bins setValue:[NSArray array] forKey:dayString];
			
			NSDate * oldDay = day;
			
			day = [[NSDate alloc] initWithTimeInterval:(60 * 60 * 24) sinceDate:oldDay];
			
			[oldDay release];
		}
		
		[day release];
	}
	
	[dateFormatter release];
	
	categoryCount = [[bins allKeys] count];
	
	maxCategoryCount = 0;
	
	for (id key in [bins allKeys])
	{
		NSUInteger thisCount = [[bins valueForKey:key] count];
		
		if (thisCount > maxCategoryCount)
			maxCategoryCount = thisCount;
	}
}

- (void) awakeFromNib
{
	[[self window] setAcceptsMouseMovedEvents:YES];
	
	[tasks addObserver:self forKeyPath:@"arrangedObjects" options:0 context:NULL];
	[tasks addObserver:self forKeyPath:@"selection" options:0 context:NULL];
	
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	
	[defaults addObserver:self forKeyPath:@"hist_background" options:0 context:NULL];
	[defaults addObserver:self forKeyPath:@"hist_default_color" options:0 context:NULL];
	[defaults addObserver:self forKeyPath:@"hist_selection_color" options:0 context:NULL];

	[defaults addObserver:self forKeyPath:@"hist_attribute" options:0 context:NULL];
	[defaults addObserver:self forKeyPath:@"hist_ignore_missing" options:0 context:NULL];
	
	[[self window] setAcceptsMouseMovedEvents:YES];
	[[self window] setIgnoresMouseEvents:NO];
	
	[self stuffBins];
}

- (void) observeValueForKeyPath: (NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
	if ([keyPath isEqual:@"selection"])
	{
		for (HistogramBlockView * v in blocks)
		{
			id last = [[tasks selectedObjects] lastObject];
			
			if (last == nil || v.task != last)
				v.highlighted = [NSNumber numberWithBool:NO];
			else
				v.highlighted = [NSNumber numberWithBool:YES];
			
			[v setNeedsDisplay:YES];
		}
	}
	else if ([keyPath isEqual:@"arrangedObjects"] || [keyPath isEqual:@"hist_attribute"] || 
			 [keyPath isEqual:@"hist_ignore_missing"])
	{
		[self stuffBins];
	}
	
	[self setNeedsDisplay:YES];
}

- (BOOL) acceptsFirstResponder
{
	return YES;
}

- (IBAction) settings:(id) sender
{
	[NSApp beginSheet:settings modalForWindow:[self window] modalDelegate:self didEndSelector:nil contextInfo:NULL];
}

- (IBAction) closeSettings:(id) sender;
{
	[NSApp endSheet:settings];
	[settings orderOut:self];
}

- (void) editTask
{
	[taskManager editTask:self];
}

- (void) keyDown:(NSEvent *)theEvent
{
	unsigned short code = [theEvent keyCode];
	
	if (code >= 123 && code <= 126 && [blocks count] > 0)
	{
		HistogramBlockView * highlighted = nil;
		
		for (HistogramBlockView * v in blocks)
		{
			if ([v.highlighted boolValue])
				highlighted = v;
		}
		
		if (highlighted != nil)
		{
			NSUInteger index = [blocks indexOfObject:highlighted];
			
			if (code == 126)
			{
				HistogramBlockView * next = [blocks objectAtIndex:0];
				
				if (index < ([blocks count] - 1))
					next = [blocks objectAtIndex:(index + 1)];
				
				[tasks setSelectedObjects:[NSArray arrayWithObject:next.task]];
			}
			else if (code == 125)
			{
				HistogramBlockView * next = [blocks objectAtIndex:0];
				
				if (index > 0)
					next = [blocks objectAtIndex:(index - 1)];
				else
					next = [blocks lastObject];
				
				[tasks setSelectedObjects:[NSArray arrayWithObject:next.task]];
			}
			else
			{
				NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
				[dateFormatter setDateFormat:@"yyyy-MM-dd"];

				NSDictionary * task = highlighted.task;

				NSString * attribute = @"   Unknown Property";
				
				NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
				if ([defaults stringForKey:@"hist_attribute"] != nil)
					attribute = [defaults stringForKey:@"hist_attribute"];

				NSArray * keys = [[bins allKeys] sortedArrayUsingSelector:@selector(compare:)];
				
				NSUInteger x = 0;
				
				for (NSUInteger i = 0; i < [keys count]; i++)
				{
					id value = [task valueForKey:attribute];
					
					if ([value isKindOfClass:[NSDate class]])
						value = [dateFormatter stringFromDate:value];
					
					if (value != nil && [value isEqual:[keys objectAtIndex:i]])
						x = i;
				}
				
				NSArray * bar = [bins valueForKey:[keys objectAtIndex:x]];
				
				NSUInteger y = [bar indexOfObject:task];
				
				if (code == 123)
				{
					if (x == 0)
						x = [keys count] - 1;
					else
						x = x - 1;
					
					while ([[bins valueForKey:[keys objectAtIndex:x]] count] == 0)
					{
						if (x == 0)
							x = [keys count] - 1;
						else
							x = x - 1;
					}
				}
				else
				{
					if (x < [keys count] - 1)
						x = x + 1;
					else
						x = 0;

					while ([[bins valueForKey:[keys objectAtIndex:x]] count] == 0)
					{
						if (x < [keys count] - 1)
							x = x + 1;
						else
							x = 0;
					}
				}
				
				NSArray * nextBar = [bins valueForKey:[keys objectAtIndex:x]];
				
				if (y >= [nextBar count] - 1)
					y = [nextBar count] - 1;
				
				[tasks setSelectedObjects:[NSArray arrayWithObject:[nextBar objectAtIndex:y]]];
				
				[dateFormatter release];
			}
		}
		else
		{
			HistogramBlockView * next = [blocks objectAtIndex:0];
			[tasks setSelectedObjects:[NSArray arrayWithObject:next.task]];
		}
	}
	else
		[super keyDown:theEvent];
}

@end
